/*
 * Scanline.cpp
 *
 *  Created on: 27/09/2009
 *      Author: ale
 */

#include "Scanline.h"
#include <cstdlib>
#include <GL/gl.h>
#include "Definiciones.h"
#include <iostream>
Scanline::Scanline() {

}

Scanline::~Scanline() {
}

void Scanline::insertarArista(Arista *list, Arista *arista) {
	Arista *p, *q = list;
	p = q->siguiente;
	while (p != NULL) {

		if (arista->xIntersec < p->xIntersec)
			p = NULL;
		else {
			q = p;
			p = p->siguiente;
		}
	}
	arista->siguiente = q->siguiente;
	q->siguiente = arista;
}

int Scanline::ySiguiente(int k, const std::vector<Punto>& pts) {
	int j;
	int cnt = pts.size();
	if ((k + 1) > (cnt - 1))
		j = 0;
	else
		j = k + 1;
	while (pts[k].y() == pts[j].y()) {
		if ((j + 1) > (cnt - 1))
			j = 0;
		else
			j++;
	}
	return (pts[j].y());
}

void Scanline::makeAristaRec(Punto& inferior, Punto& superior, int yComp,
		Arista *arista, Arista *aristas[]) {
	arista->dxPorScan = (float) (superior.x() - inferior.x()) / (superior.y()
			- inferior.y());
	arista->xIntersec = inferior.x();
	if (superior.y() < yComp) {
		arista->ySup = superior.y() - 1;
	} else {
		arista->ySup = superior.y();
	}

	if (inferior.y() < ALTO && inferior.y() > 0) {
		insertarArista(aristas[inferior.y()], arista);
	} else {
		if (inferior.y() > ALTO) {
			insertarArista(aristas[ALTO - 1], arista);
		} else {
			insertarArista(aristas[0], arista);
		}
	}
}

void Scanline::armarListaAristas(const std::vector<Punto> &pts,
		Arista *aristas[]) {
	Arista *arista;
	int cnt = pts.size();
	Punto v1, v2;
	int i, yPrev = pts[cnt - 2].y();
	v1.x(pts[cnt - 1].x());
	v1.y(pts[cnt - 1].y());
	for (i = 0; i < cnt; i++) {
		v2 = pts[i];
		if (v1.y() != v2.y()) {
			arista = (Arista *) malloc(sizeof(Arista));
			if (v1.y() < v2.y())
				makeAristaRec(v1, v2, ySiguiente(i, pts), arista, aristas);
			else
				makeAristaRec(v2, v1, yPrev, arista, aristas);
		}
		yPrev = v1.y();
		v1 = v2;
	}
}

void Scanline::armarListaActivas(int scan, Arista *activa, Arista *aristas[]) {
	Arista *p, *q;
	p = aristas[scan]->siguiente;
	while (p) {
		q = p->siguiente;
		insertarArista(activa, p);
		p = q;
	}
}

void Scanline::deleteSiguiente(Arista *q) {
	Arista *p = q->siguiente;
	q->siguiente = p->siguiente;
	free(p);
}

void Scanline::updateListaActivas(int scan, Arista *activa) {
	Arista *q = activa, *p = activa->siguiente;
	while (p)
		if (scan >= p->ySup) {
			p = p->siguiente;
			deleteSiguiente(q);
		} else {
			p->xIntersec = p->xIntersec + p->dxPorScan;
			q = p;
			p = p->siguiente;
		}
}

void Scanline::reordenarListaActivas(Arista *activa) {
	Arista *q, *p = activa->siguiente;
	activa->siguiente = NULL;
	while (p) {
		q = p->siguiente;
		insertarArista(activa, p);
		p = q;
	}
}

void Scanline::fillScan(int scan, Arista *activa) {
	Arista *p1, *p2;
	int i;
	p1 = activa->siguiente;
	while (p1 && p1->siguiente) {
		p2 = p1->siguiente;
		for (i = p1->xIntersec; i < p2->xIntersec; i++) {
			glBegin(GL_POINTS);
			glVertex2i((int) i, scan);
			glEnd();
		}
		p1 = p2->siguiente;
	}
}

void Scanline::rellenar(const std::vector<Punto>& pts) {
	Arista *aristas[ALTO], *activa;
	int i, scan;
	for (i = 0; i < ALTO; i++) {
		aristas[i] = (Arista *) malloc(sizeof(Arista));
		aristas[i]->siguiente = NULL;
	}
	armarListaAristas(pts, aristas);
	activa = (Arista *) malloc(sizeof(Arista));
	activa->siguiente = NULL;
	for (scan = 0; scan < ALTO; scan++) {
		armarListaActivas(scan, activa, aristas);
		if (activa->siguiente) {
			fillScan(scan, activa);
			updateListaActivas(scan, activa);
			reordenarListaActivas(activa);
		}
	}
	for (i = 0; i < ALTO; i++)
		free(aristas[i]);
}
